/*********************************************************************
 * 						computer graphics
 * 							exercise 4
 * 					shaham fridenberg 039421268
 *                    danielle barda 304954654
 ********************************************************************/

/* This code was written by Oren Kapah and changed and updated by shaham and danielle ;) */
/**
 */
import java.awt.Frame;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;

import com.sun.opengl.util.Animator;
import com.sun.opengl.util.texture.Texture;
import com.sun.opengl.util.texture.TextureIO;




 
public class Game3D implements GLEventListener, KeyListener {
    
	// GLU object
    static GLU glu = new GLU();
    
    // canvas, frame and animator
    static GLCanvas canvas = new GLCanvas();
    static Frame frame = new Frame("best game in the world ;)");
    static Animator animator = new Animator(canvas);
    public int room;
    
    // angles and coordinates of look
    double	alpha = -90, alpha_step = 5, betha = 0, betha_step = 5;
    double	eyeX=1500, eyeY=250, eyeZ=1500;
    double	upX=0, upY=1, upZ=0;
  
    // delta for walking use
    double	step=10;
    
    int speed = 10;
    
    int numOfRooms = 5;
    
    // balls for first room
    Ball ball, ball2, ball3, ball4;
    int ballIndex, ballIndex2, ballIndex3, ballIndex4;
    int direction = 1, direction2 = -1, direction3 = 1, direction4 = -1;
    Vector b1 = new Vector(1500,250,-1500);
    Vector b2 = new Vector(1500,250,-2100);
    Vector b3 = new Vector(1500,250,-2700);
    Vector b4 = new Vector(1500,250,-3300);
    
    // balls for second room
    Ball iceBall, iceBall2;
    int iceIndex, iceIndex2;
    Vector ib = new Vector(1500,250,-7000);
    Vector ib2 = new Vector(1500,250,-7500);
    
    Cone [] cones1 = new Cone[5];
    int [] coneIndex1 = new int[5];
    
    Cone [] cones2 = new Cone[5];
    int [] coneIndex2 = new int[5];
    
    Cone [] cones3 = new Cone[5];
    int [] coneIndex3 = new int[5];
    
    Cone [] cones4 = new Cone[5];
    int [] coneIndex4 = new int[5];
    
    Cone [] cones5 = new Cone[5];
    int [] coneIndex5 = new int[5];
    
    Roll [] roll1=new Roll[5];
    int [] rollIndex1= new int[5];
    
    Roll [] roll2=new Roll[5];
    int [] rollIndex2= new int[5];

    Roll [] roll3=new Roll[5];
    int [] rollIndex3= new int[5];
    
    Roll [] roll4=new Roll[5];
    int [] rollIndex4= new int[5];
    
    // size of typical room
	double	sizeX=3000, sizeY=900, sizeZ=3000;
	double corridorSize = 1000;
	double zUpperLimit = -(numOfRooms * corridorSize + (numOfRooms-1) * sizeZ + 9000); 
	
    double delta = 100.0;
	
    float rRate = 0;
    float rRate2 = 180;
    float tRate = 0;
    float tRate2 = 0;
    float tRate3 = 0;
    float tRate4 = 0;
    boolean win = false;
    
    //how much move in y axis
    float coneRate1 = 0;
    float coneRate2 = -1000;
    int dirCone = -1;
    int dirCone2 = 1;
    boolean coneUp1 = true;
    boolean coneUp2 = false;
    
    float rollRate1 = 0;
    float rollRate2 = 1800;
    int dirRoll1 = -1;
    int dirRoll2 = 1;
    boolean rollLeft = true;
    boolean rollRight = true;
    
    int xLimit1=1800;
    int xLimit2=3000;
    
    float[] colorWhite  = {1.0f,1.0f,1.0f,1.0f};
    
    // textures
    Texture floor, wall, wall2, sky, ceil2, floor2, carpet, books, shelf, pic;
    Texture wall3, wall4, wall5, wall6, cab, trophy, black, fire, ice, fireRoom, iceRoom, sandRoom, sand,hiveroom,hive;
    
    // sinus function
	public double SIN(double x)
	{
		return java.lang.Math.sin(x*3.14159/180);
	}
	
	// cosinus function
	public double COS(double x)
	{
		return java.lang.Math.cos(x*3.14159/180);
	}
	
	// display function
    public void display(GLAutoDrawable gLDrawable) {
    	
    	// get gl instance
		final GL gl = gLDrawable.getGL();
		
		float	material[] = {0.9f,0f,1f,1.0f}; // 1- the current  2-?  3- left  4-? how much light the object will ret
    	float	position0[] = {210f,0f,-4000f,1.0f};		// red light on the right side (light 0)
    	float	position1[] = {190f,0f,-4000f,1.0f};	// blue light on the left side (light 1)
    	
    	//1-  where on the z   2
    	
    	 float posLight2[] = { 200f, 0.f, -2000.f, 1.0f };
    	
		gl.glClear(GL.GL_COLOR_BUFFER_BIT);
		gl.glClear(GL.GL_DEPTH_BUFFER_BIT);
		gl.glLoadIdentity();
		
		// set parameters of look
		glu.gluLookAt(eyeX,eyeY,eyeZ,eyeX+COS(alpha)*COS(betha),eyeY+SIN(betha),eyeZ+SIN(alpha)*COS(betha),upX,upY,upZ); //set the camera view
		
		Vector cur = new Vector(eyeX, eyeY, eyeZ);
		gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, position0, 0);
        gl.glLightfv(GL.GL_LIGHT1, GL.GL_POSITION, position1, 0);
        gl.glLightfv(GL.GL_LIGHT2, GL.GL_POSITION, posLight2, 0);
        
        gl.glPushMatrix();
        gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT_AND_DIFFUSE, material, 0);
//        gl.glBegin(GL.GL_QUADS);
		
		
		//System.out.println("x: " + b3.x);
    	
    	if(distance(cur, b1) < 200 || distance(cur, b2) < 200 || distance(cur, b3) < 200 || distance(cur, b4) < 200)
    	{
    		eyeX = 1500;
    		eyeY = 250;
    		eyeZ = 1500;
    		upX=0; 
    		upY=1; 
    		upZ=0;
    		alpha = -90;
    		betha = 0;	
    	}
		
		if(distance(cur, ib) < 200 || distance(cur, ib2) < 200)
		{
			eyeX = 1500;
    		eyeY = 250;
    		eyeZ = 1500;
    		upX=0; 
    		upY=1; 
    		upZ=0;
    		alpha = -90;
    		betha = 0;
		}
		
		if(((eyeZ < -9100 && eyeZ > -9370) || (eyeZ < -9840 && eyeZ > -10110) || (eyeZ < -10580 && eyeZ > -10850)) && coneUp1)
		{
			eyeX = 1500;
    		eyeY = 250;
    		eyeZ = 1500;
    		upX=0; 
    		upY=1; 
    		upZ=0;
    		alpha = -90;
    		betha = 0;
		}
		
		if(((eyeZ < -9470 && eyeZ > -9740) || (eyeZ < -10210 && eyeZ > -10480)) && coneUp2)
		{
			eyeX = 1500;
    		eyeY = 250;
    		eyeZ = 1500;
    		upX=0; 
    		upY=1; 
    		upZ=0;
    		alpha = -90;
    		betha = 0;
		}
    	
		if(((eyeZ < -13930 && eyeZ > -14530) && (eyeX>=0 && eyeX<=xLimit1)))
		{
			eyeX = 1500;
    		eyeY = 250;
    		eyeZ = 1500;
    		upX=0; 
    		upY=1; 
    		upZ=0;
    		alpha = -90;
    		betha = 0;
		}
    	
//		System.out.println("xLimit2 = "+xLimit2);
		if(((eyeZ < -15630 && eyeZ > -16230) && (eyeX>=xLimit2 && eyeX<=3000)))
		{
			eyeX = 1500;
    		eyeY = 250;
    		eyeZ = 1500;
    		upX=0; 
    		upY=1; 
    		upZ=0;
    		alpha = -90;
    		betha = 0;
		}
    	
		
		rRate++;
		if(rRate == 360)
			rRate = 0;
		
		rRate2+=1.5;
		if(rRate2 == 360)
			rRate2 = 0;
		
		gl.glCallList(room);
		
		if(direction == 1)
		{
			tRate+=10;
			b1.x += 10;
		}
		else
		{
			tRate-=10;
			b1.x -=10;
		}
		if(tRate > 1200)
			direction = -1;
		if(tRate < -1200)
			direction = 1;
		
		if(direction2 == 1)
		{
			tRate2+=15;
			b2.x += 15;
		}
		else
		{
			tRate2-=15;
			b2.x -= 15;
		}
		if(tRate2 > 1200)
			direction2 = -1;
		if(tRate2 < -1200)
			direction2 = 1;
		
		if(direction3 == 1)
		{
			tRate3+=20;
			b3.x += 20;
		}
		else
		{
			tRate3-=20;
			b3.x -= 20;
		}
		if(tRate3 > 1200)
			direction3 = -1;
		if(tRate3 < -1200)
			direction3 = 1;
		
		
		if(direction4 == 1)
		{
			tRate4+=25;
			b4.x += 25;
		}
		else
		{
			tRate4-=25;
			b4.x -= 25;
		}
		if(tRate4 > 1200)
			direction4 = -1;
		if(tRate4 < -1200)
			direction4 = 1;
		//if we up +5
		if(dirCone == 1)
			coneRate1+=5;
		else
			coneRate1-=5;
		if(coneRate1 == 0)
			dirCone = -1;
		else if(coneRate1 == -1000)
			dirCone = 1;
		if(coneRate1 > -300)
			coneUp1 = true;
		else
			coneUp1 = false;
		
		if(dirCone2 == 1)
			coneRate2+=5;
		else
			coneRate2-=5;
		if(coneRate2 == 0)
			dirCone2 = -1;
		else if(coneRate2 == -1000)
			dirCone2 = 1;
		if(coneRate2 > -300)
			coneUp2 = true;
		else
			coneUp2 = false;

		/**
		 * for Rolls Right
		 */
		if(dirRoll1 == 1)
		{
			rollRate1+=10;
			xLimit1+=10;
		}
		else
		{
			rollRate1-=10;
			xLimit1-=10;
		}
		
		if(rollRate1==0)
			dirRoll1=-1;
		
		else if(rollRate1 == -1800)
			dirRoll1 = 1;
		
		if(rollRate1 ==0)
			 rollRight= true;
		else
			rollRight= false;
		
		/**
		 * for Rolls Left
		 */
		if(dirRoll2 == 1)
		{
			rollRate2-=10;
			xLimit2-=10;
		}
		else
		{
			rollRate2+=10;
			xLimit2+=10;
		}
		
		if(rollRate2==0)
			dirRoll2=-1;
		
		else if(rollRate2 == 1800)
			dirRoll2 =1;
		
		if(rollRate2 ==0)
			 rollLeft= true;
		else
			rollLeft= false;
		
		gl.glPushMatrix();
		gl.glTranslated(1500,250,-1700);
		//gl.glRotated(rotateRate, 0, 1, 0);
		gl.glTranslated(tRate,0, 200);
		gl.glCallList(ballIndex);	
		gl.glPopMatrix();
	
		gl.glPushMatrix();
		gl.glTranslated(1500,250,-2300);
		//gl.glRotated(rotateRate, 0, 1, 0);
		gl.glTranslated(tRate2,0, 200);
		gl.glCallList(ballIndex2);	
		gl.glPopMatrix();
		
		gl.glPushMatrix();
		gl.glTranslated(1500,250,-2900);
		//gl.glRotated(rotateRate, 0, 1, 0);
		gl.glTranslated(tRate3,0, 200);
		gl.glCallList(ballIndex3);	
		gl.glPopMatrix();
		
		gl.glPushMatrix();
		gl.glTranslated(1500,250,-3500);
		//gl.glRotated(rotateRate, 0, 1, 0);
		gl.glTranslated(tRate4,0, 200);
		gl.glCallList(ballIndex4);	
		gl.glPopMatrix();
		
		gl.glPushMatrix();
		gl.glTranslated(1500,250,-6500);
		gl.glRotated(rRate, 0, 1, 0);
		gl.glTranslated(0,0, -500);
		gl.glCallList(iceIndex);	
		gl.glPopMatrix();
		
		gl.glPushMatrix();
		gl.glTranslated(1500,250,-6500);
		gl.glRotated(rRate2, 0, 1, 0);
		gl.glTranslated(0,0, -1000);
		gl.glCallList(iceIndex2);	
		gl.glPopMatrix();
		
		ib.x = 1500 - 500 * (float)SIN(rRate);
		ib.z = -6500 - 500 * (float)COS(rRate);
		
		ib2.x = 1500 - 1000 * (float)SIN(rRate2);
		ib2.z = -6500 - 1000 * (float)COS(rRate2);
		
		int xDiff = 580;
		for(int i=0; i<5; i++)
		{
			gl.glPushMatrix();
			gl.glTranslated(340+i*xDiff,0,-9340);
			gl.glRotated(270, 1, 0, 0);
			gl.glTranslated(0,0, coneRate1);
			gl.glCallList(coneIndex1[i]);	
			gl.glPopMatrix();
		}
		
		for(int i=0; i<5; i++)
		{
			gl.glPushMatrix();
			gl.glTranslated(340+i*xDiff,0,-9920);
			gl.glRotated(270, 1, 0, 0);
			gl.glTranslated(0,0, coneRate2);
			gl.glCallList(coneIndex2[i]);	
			gl.glPopMatrix();
		}
		
		for(int i=0; i<5; i++)
		{
			gl.glPushMatrix();
			gl.glTranslated(340+i*xDiff,0,-10500);
			gl.glRotated(270, 1, 0, 0);
			gl.glTranslated(0,0, coneRate1);
			gl.glCallList(coneIndex3[i]);	
			gl.glPopMatrix();
		}
		
		for(int i=0; i<5; i++)
		{
			gl.glPushMatrix();
			gl.glTranslated(340+i*xDiff,0,-11080);
			gl.glRotated(270, 1, 0, 0);
			gl.glTranslated(0,0, coneRate2);
			gl.glCallList(coneIndex4[i]);	
			gl.glPopMatrix();
		}
		
		for(int i=0; i<5; i++)
		{
			gl.glPushMatrix();
			gl.glTranslated(340+i*xDiff,0,-11660);
			gl.glRotated(270, 1, 0, 0);
			gl.glTranslated(0,0, coneRate1);
			gl.glCallList(coneIndex5[i]);	
			gl.glPopMatrix();
		}
		
		
		/******************For Rolls :*****************************************************/
		//Right
		for(int i=0; i<3; i++)
		{
			gl.glPushMatrix();
			gl.glTranslated(300+i*600,0,-14230);
			gl.glRotated(270, 1, 0, 0);
			gl.glTranslated(rollRate1,0,0);
			gl.glCallList(rollIndex1[i]);	
			gl.glPopMatrix();
		}
		
		

		
		//LEFT
		for(int i=0; i<3; i++)
		{
			gl.glPushMatrix();
			gl.glTranslated(2700-i*600,0,-15930);
			gl.glRotated(270, 1, 0, 0);
			gl.glTranslated(rollRate2,0,0);
			gl.glCallList(rollIndex2[i]);	
			gl.glPopMatrix();
		}
		

		
		//System.out.println("x: " + iceBall.x + " y: " + iceBall.y + " z: " + iceBall.z);
		
    }
       
    // init function
    public void init(GLAutoDrawable gLDrawable) {
    	
    	// get gl instance
		GL gl = gLDrawable.getGL();
		
		// set gl parameters
		gl.glShadeModel(GL.GL_SMOOTH);
		gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
		gl.glClearDepth(1.0f);
		gl.glEnable(GL.GL_DEPTH_TEST);
		gl.glDepthFunc(GL.GL_LEQUAL);
		gl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);
		
		// enable texture
		gl.glEnable(GL.GL_TEXTURE_2D);
		
		
		
		
		// init textures
		initTextures();
        
		// Light
    	float	ambient[] = {0.375f,0.9f,0.55f,1.0f};
    	float	diffuse0[] = {1f,0f,0f,1.0f};
    	float	diffuse1[] = {0f,0f,1f,1.0f};
    	
    	
    	float	ambient2[] = {0.2f,0.2f,0.2f,1.0f};
    	float	diffuse3[] = {0.2f,0.2f,0.2f,1.0f};
    	
    	gl.glShadeModel(GL.GL_SMOOTH); 
		
        gl.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT, ambient, 0);
        gl.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, diffuse0, 0);
        gl.glEnable(GL.GL_LIGHT0);
        
        gl.glLightfv(GL.GL_LIGHT1, GL.GL_AMBIENT, ambient, 0);
        gl.glLightfv(GL.GL_LIGHT1, GL.GL_DIFFUSE, diffuse1, 0);
        gl.glEnable(GL.GL_LIGHT1);
        
        
//        gl.glLightfv(GL.GL_LIGHT2, GL.GL_AMBIENT, ambient2, 0);
//        gl.glLightfv( GL.GL_LIGHT2, GL.GL_DIFFUSE, diffuse3, 0 );
        
        gl.glEnable( GL.GL_LIGHT2 );
        
        gl.glEnable(GL.GL_LIGHTING);
        
		
		
        // set the parameters for the texture
        gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_REPEAT);
        gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_REPEAT);
        
		// make the maze
		room = make_maze(gl);
		
		ball = new Ball(0,0,0,200, fire);
		ball2 = new Ball(0,0,0,200, fire);
		ball3 = new Ball(0,0,0,200, fire);
		ball4 = new Ball(0,0,0,200, fire);
		ballIndex = ball.draw(gl, glu);
		ballIndex2 = ball2.draw(gl, glu);
		ballIndex3 = ball3.draw(gl, glu);
		ballIndex4 = ball4.draw(gl, glu);
		
		iceBall = new Ball(0,0,0,200,ice);
		iceBall2 = new Ball(0,0,0,200,ice);
		iceIndex = iceBall.draw(gl, glu);
		iceIndex2 = iceBall2.draw(gl, glu);
		
		for(int i=0; i<5; i++)
		{
			cones1[i] = new Cone(0,0,0,200,sand);
			coneIndex1[i] = cones1[i].draw(gl, glu);
			
			cones2[i] = new Cone(0,0,0,200,sand);
			coneIndex2[i] = cones1[i].draw(gl, glu);
			
			cones3[i] = new Cone(0,0,0,200,sand);
			coneIndex3[i] = cones1[i].draw(gl, glu);
			
			cones4[i] = new Cone(0,0,0,200,sand);
			coneIndex4[i] = cones1[i].draw(gl, glu);
			
			cones5[i] = new Cone(0,0,0,200,sand);
			coneIndex5[i] = cones1[i].draw(gl, glu);	
			
			roll1[i] = new Roll(0,0,0,200,hive);
			rollIndex1[i] = roll1[i].draw(gl, glu);
			
			roll2[i] = new Roll(0,0,0,200,hive);
			rollIndex2[i] = roll2[i].draw(gl, glu);
			
			roll3[i] = new Roll(00,0,0,200,hive);
			rollIndex3[i] = roll3[i].draw(gl, glu);
			
			roll4[i] = new Roll(0,0,0,200,hive);
			rollIndex4[i] = roll4[i].draw(gl, glu);
			
			
		}
		
		
		gLDrawable.addKeyListener(this);
    }
 
    // make the maze in which the game takes place
	public int make_maze(GL	gl)
	{
		int list;
		
		list = gl.glGenLists(1);
		gl.glNewList(list, GL.GL_COMPILE);
		
		// set texture parameters
		gl.glTexParameteri( GL.GL_TEXTURE_2D,GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT );
		gl.glTexParameteri( GL.GL_TEXTURE_2D,GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT );
       
		
		
		
		// create start and size vectors
		Vector start = new Vector(0,0,0);
		Vector roomSize = new Vector(sizeX, sizeY, sizeZ);
		Vector frontSize = new Vector(sizeX/3, sizeY, sizeZ);
		Vector corridorSize = new Vector(1000,sizeY,1000);
		int zLocation = -4000;
		int corridorZ = -1000;
		
		// make first room with gateway at its front
		makeFront(gl, start, frontSize, wall);
		makeFront(gl, new Vector(2000,0,0), frontSize, wall);
		makeBack(gl, start, roomSize, wall);
		makeLeft(gl, start, roomSize, wall);
		makeRight(gl, start, roomSize, wall);
		makeCeil(gl, start, roomSize, ceil2);
		makeFloor(gl, start, roomSize, floor);
		
		// make table at the left of the room
//		makeSquare(gl,new Vector(150,0,500), new Vector(50,150,50), carpet);
//		makeSquare(gl,new Vector(150,0,1000), new Vector(50,150,50), carpet);
//		makeSquare(gl,new Vector(400,0,500), new Vector(50,150,50), carpet);
//		makeSquare(gl,new Vector(400,0,1000), new Vector(50,150,50), carpet);
//		makeSquare(gl,new Vector(150,150,500), new Vector(300,20,550), carpet);
		
//		// make book shelf at the right of the room
//		int y=0;
//		for(int i=0; i<6; i++)
//		{
//			makeBookShelf(gl,new Vector(2150,y,0), new Vector(400,100,250), shelf, books);
//			y+=100;
//		}
//		
		// make picture on the wall of room
		makeSquare(gl,new Vector(400,500,0), new Vector(400,250,5), pic);
		
		// make cabinet at second room
//		makeBookShelf(gl,new Vector(200,0,-4000), new Vector(300,625,250), carpet, cab);
		
		// make another rooms 
		for(int i=0; i<numOfRooms; i++)
		{
			
			// make corridor
			if(i==0)
			{
				makeLeft(gl, new Vector(1000,0,corridorZ), corridorSize, fireRoom);
				makeRight(gl, new Vector(1000,0,corridorZ), corridorSize, fireRoom);
				makeCeil(gl, new Vector(1000,0,corridorZ), corridorSize, fireRoom);
				makeFloor(gl, new Vector(1000,0,corridorZ), corridorSize, fireRoom);
			}
			else if(i==1)
			{
				makeLeft(gl, new Vector(1000,0,corridorZ), corridorSize, iceRoom);
				makeRight(gl, new Vector(1000,0,corridorZ), corridorSize, iceRoom);
				makeCeil(gl, new Vector(1000,0,corridorZ), corridorSize, iceRoom);
				makeFloor(gl, new Vector(1000,0,corridorZ), corridorSize, iceRoom);
			}
			else if(i==2)
			{
				makeLeft(gl, new Vector(1000,0,corridorZ), corridorSize, sandRoom);
				makeRight(gl, new Vector(1000,0,corridorZ), corridorSize, sandRoom);
				makeCeil(gl, new Vector(1000,0,corridorZ), corridorSize, sandRoom);
				makeFloor(gl, new Vector(1000,0,corridorZ), corridorSize, sandRoom);
			}
			else if(i==3)
			{
				makeLeft(gl, new Vector(1000,0,corridorZ), corridorSize, sandRoom);
				makeRight(gl, new Vector(1000,0,corridorZ), corridorSize, sandRoom);
				makeCeil(gl, new Vector(1000,0,corridorZ), corridorSize, sandRoom);
				makeFloor(gl, new Vector(1000,0,corridorZ), corridorSize, sandRoom);
			}
			
			else if(i==4)
			{
				makeLeft(gl, new Vector(1000,0,corridorZ), corridorSize, hiveroom);
				makeRight(gl, new Vector(1000,0,corridorZ), corridorSize, hiveroom);
				makeCeil(gl, new Vector(1000,0,corridorZ), corridorSize, hiveroom);
				makeFloor(gl, new Vector(1000,0,corridorZ), corridorSize, hiveroom);
			}
			
			corridorZ-=4000;
			start = new Vector(0,0,zLocation);
			
			// last room is a big garden
			if(i==(numOfRooms-1))
			{
				start = new Vector(-3000,0,zLocation-6000);
				roomSize = new Vector(9000,900,9000);
				frontSize = new Vector(4000,900,9000);
				
				makeFront(gl, start, roomSize, wall);
				makeBack(gl, new Vector(2000,0,zLocation-6000), frontSize, wall);
				makeCeil(gl, start, roomSize, sky);
				makeFloor(gl, start, roomSize, floor2);
				makeBack(gl, start, frontSize, wall);
				makeLeft(gl, start, roomSize, wall);
				makeRight(gl, start, roomSize, wall);
				
			}
			
			// any regular room on the way
			else
			{
				if(i==0)
				{
					makeFront(gl, start, frontSize, fireRoom);
					makeFront(gl, new Vector(2000,0,zLocation), frontSize, fireRoom);
					makeBack(gl, new Vector(2000,0,zLocation), frontSize, fireRoom);
					makeCeil(gl, start, roomSize, fireRoom);
					makeFloor(gl, start, roomSize, fireRoom);
					makeBack(gl, start, frontSize, fireRoom);
					makeLeft(gl, start, roomSize, fireRoom);
					makeRight(gl, start, roomSize, fireRoom);
				}
				else if(i==1)
				{
					makeFront(gl, start, frontSize, iceRoom);
					makeFront(gl, new Vector(2000,0,zLocation), frontSize, iceRoom);
					makeBack(gl, new Vector(2000,0,zLocation), frontSize, iceRoom);
					makeCeil(gl, start, roomSize, iceRoom);
					makeFloor(gl, start, roomSize, iceRoom);
					makeBack(gl, start, frontSize, iceRoom);
					makeLeft(gl, start, roomSize, iceRoom);
					makeRight(gl, start, roomSize, iceRoom);
				}
				else if(i==2)
				{
					makeFront(gl, start, frontSize, sandRoom);
					makeFront(gl, new Vector(2000,0,zLocation), frontSize, sandRoom);
					makeBack(gl, new Vector(2000,0,zLocation), frontSize, sandRoom);
					makeCeil(gl, start, roomSize, sandRoom);
					makeFloor(gl, start, roomSize, sandRoom);
					makeBack(gl, start, frontSize, sandRoom);
					makeLeft(gl, start, roomSize, sandRoom);
					makeRight(gl, start, roomSize, sandRoom);
				}	
				else if(i==3)
				{
					makeFront(gl, start, frontSize, hiveroom);
					makeFront(gl, new Vector(2000,0,zLocation), frontSize, hiveroom);
					makeBack(gl, new Vector(2000,0,zLocation), frontSize, hiveroom);
					makeCeil(gl, start, roomSize, hiveroom);
					makeFloor(gl, start, roomSize, hiveroom);
					makeBack(gl, start, frontSize, hiveroom);
					makeLeft(gl, start, roomSize, hiveroom);
					makeRight(gl, start, roomSize, hiveroom);
				}
			}
			
			zLocation-=4000;
		}
		
		
		Vector tStart = new Vector(1300,0,zUpperLimit+3000);
		Vector tSize = new Vector(400, 400, 250);
		makeBookShelf(gl, tStart, tSize, black, trophy);
		
		
	    gl.glEndList();
	    return list;
	}

	// make square object for any use
	public void makeSquare(GL gl, Vector start, Vector size, Texture texture)
	{
		// make all 6 faces of object
		makeFront(gl, start, size, texture);
		makeBack(gl, start, size, texture);
		makeLeft(gl, start, size, texture);
		makeRight(gl, start, size, texture);
		makeCeil(gl, start, size, texture);
	}
	
	// make square object with two different textures
	public void makeBookShelf(GL gl, Vector start, Vector size, Texture texture1, Texture texture2)
	{
		makeFront(gl, start, size, texture1);
		makeBack(gl, start, size, texture2);
		makeLeft(gl, start, size, texture1);
		makeRight(gl, start, size, texture1);
		makeCeil(gl, start, size, texture1);
	}

	// make upper face of any squre object mainly foor rooms creation
	public void makeCeil(GL gl, Vector start, Vector size, Texture tex)
	{
		// bind current texture
		tex.bind();
		
		// create quad strip
		gl.glBegin(GL.GL_QUAD_STRIP);
		
			gl.glTexCoord2f(0.0f, 0.0f);
			gl.glVertex3d(start.x, start.y+size.y, start.z);
			gl.glTexCoord2f(0.0f, 1.0f);
			gl.glVertex3d(start.x+size.x, start.y+size.y, start.z);
	
			gl.glTexCoord2f(1.0f, 0.0f);
			gl.glVertex3d(start.x, start.y+size.y, start.z+size.z);
			gl.glTexCoord2f(1.0f, 1.0f);
			gl.glVertex3d(start.x+size.x, start.y+size.y, start.z+size.z);
			
		gl.glEnd();
	}
	
	// make lower face of any squre object mainly foor rooms creation
	public void makeFloor(GL gl, Vector start, Vector size, Texture tex)
	{
		// bind current texture
		tex.bind();
		
		// create quad strip
		gl.glBegin(GL.GL_QUAD_STRIP);
		
			gl.glTexCoord2f(0.0f, 0.0f);
			gl.glVertex3d(start.x, start.y, start.z);
			gl.glTexCoord2f(0.0f, 1.0f);
			gl.glVertex3d(start.x+size.x, start.y, start.z);
	
			gl.glTexCoord2f(1.0f, 0.0f);
			gl.glVertex3d(start.x, start.y, start.z+size.z);
			gl.glTexCoord2f(1.0f, 1.0f);
			gl.glVertex3d(start.x+size.x, start.y, start.z+size.z);
			
		gl.glEnd();
	}
	
	// make front face of any squre object mainly foor rooms creation
	public void makeFront(GL gl, Vector start, Vector size, Texture tex)
	{
		// bind current texture
		tex.bind();
				
		// create quad strip
		gl.glBegin(GL.GL_QUAD_STRIP);
		
			gl.glTexCoord2f(0.0f, 0.0f);
			gl.glVertex3d(start.x, start.y, start.z);
			gl.glTexCoord2f(0.0f, 1.0f);
			gl.glVertex3d(start.x, start.y+size.y, start.z);
	
			gl.glTexCoord2f(1.0f, 0.0f);
			gl.glVertex3d(start.x+size.x, start.y, start.z);
			gl.glTexCoord2f(1.0f, 1.0f);
			gl.glVertex3d(start.x+size.x, start.y+size.y, start.z);

		gl.glEnd();
	}
	
	// make back face of any squre object mainly foor rooms creation
	public void makeBack(GL gl, Vector start, Vector size, Texture tex)
	{
		// bind current texture
		tex.bind();
		
		// create quad strip
		gl.glBegin(GL.GL_QUAD_STRIP);
		
			gl.glTexCoord2f(0.0f, 0.0f);
			gl.glVertex3d(start.x, start.y, start.z+size.z);
			gl.glTexCoord2f(0.0f, 1.0f);
			gl.glVertex3d(start.x, start.y+size.y, start.z+size.z);
	
			gl.glTexCoord2f(1.0f, 0.0f);
			gl.glVertex3d(start.x+size.x, start.y, start.z+size.z);
			gl.glTexCoord2f(1.0f, 1.0f);
			gl.glVertex3d(start.x+size.x, start.y+size.y, start.z+size.z);

		gl.glEnd();
	}
	
	// make left face of any squre object mainly foor rooms creation
	public void makeLeft(GL gl, Vector start, Vector size, Texture tex)
	{
		// bind current texture
		tex.bind();
		
		// create quad strip
		gl.glBegin(GL.GL_QUAD_STRIP);
		
			gl.glTexCoord2f(0.0f, 0.0f);
			gl.glVertex3d(start.x, start.y, start.z);
			gl.glTexCoord2f(0.0f, 1.0f);
			gl.glVertex3d(start.x, start.y+size.y, start.z);
	
			gl.glTexCoord2f(1.0f, 0.0f);
			gl.glVertex3d(start.x, start.y, start.z+size.z);
			gl.glTexCoord2f(1.0f, 1.0f);
			gl.glVertex3d(start.x, start.y+size.y, start.z+size.z);

		gl.glEnd();
	}
	
	// make right face of any squre object mainly foor rooms creation
	public void makeRight(GL gl, Vector start, Vector size, Texture tex)
	{
		// bind current texture
		tex.bind();
		
		// create quad strip
		gl.glBegin(GL.GL_QUAD_STRIP);
		
			gl.glTexCoord2f(0.0f, 0.0f);
			gl.glVertex3d(start.x+size.x, start.y, start.z);
			gl.glTexCoord2f(0.0f, 1.0f);
			gl.glVertex3d(start.x+size.x, start.y+size.y, start.z);
	
			gl.glTexCoord2f(1.0f, 0.0f);
			gl.glVertex3d(start.x+size.x, start.y, start.z+size.z);
			gl.glTexCoord2f(1.0f, 1.0f);
			gl.glVertex3d(start.x+size.x, start.y+size.y, start.z+size.z);

		gl.glEnd();
	}
	
    public void displayChanged(GLAutoDrawable gLDrawable, 
			       boolean modeChanged, boolean deviceChanged) {
    }
 
    // reshape function
    public void reshape(GLAutoDrawable gLDrawable, int x, 
			int y, int width, int height) {
		GL gl = gLDrawable.getGL();
		if(height <= 0) {
		    height = 1;
		}
		float h = (float)width / (float)height;
		gl.glMatrixMode(GL.GL_PROJECTION);
		gl.glLoadIdentity();
		
		// set perspective parameters
		glu.gluPerspective(50.0f, h, 1.0, 100000.0);
		gl.glMatrixMode(GL.GL_MODELVIEW);
		gl.glLoadIdentity();
    }
 
    // listeners to different keys
    public void keyPressed(KeyEvent e) {
    	
    	// exit game
		if(e.getKeyCode() == KeyEvent.VK_ESCAPE) {
		    exit();
		}
		
		// look up
		if(e.getKeyCode() == KeyEvent.VK_UP) {
			betha += betha_step;
			if(betha > 90 && betha < 270)
				upY = -1;
			else
				upY = 1;
			betha = betha % 360;	
		}
		
		// look down
		if(e.getKeyCode() == KeyEvent.VK_DOWN) {
			betha -= betha_step;
			if(betha > 90 && betha < 270)
				upY = -1;
			else
				upY = 1;
			betha = betha % 360;
		}
		
		// look to the left
		if(e.getKeyCode() == KeyEvent.VK_LEFT) {
		    if(upY == 1)
		    	alpha -= alpha_step;
		    else
		    	alpha += alpha_step;
		    alpha = alpha % 360;
		}
		
		// look to the right
		if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
		    if(upY == 1)
		    	alpha += alpha_step;
		    else
		    	alpha -= alpha_step;
		    alpha = alpha % 360;
		}
		
		// go straight
		if(e.getKeyCode() == KeyEvent.VK_W) {
			
			for(int i=0; i<speed; i++)
			{
				double tX = eyeX;
				double tY = eyeY;
				double tZ = eyeZ;
				
				eyeX += step*COS(alpha);
				//eyeY += step*SIN(betha);
				eyeZ += step*SIN(alpha);
				
				if(!isInBounds())
				{
					eyeX = tX;
					eyeY = tY;
					eyeZ = tZ;
				}
			}
		}
		
		// go backward
		if(e.getKeyCode() == KeyEvent.VK_S) {
			for(int i=0; i<speed; i++)
			{
				double tX = eyeX;
				double tY = eyeY;
				double tZ = eyeZ;
				
				eyeX -= step*COS(alpha);
				//eyeY -= step*SIN(betha);
				eyeZ -= step*SIN(alpha);
					
				if(!isInBounds())
				{
					eyeX = tX;
					eyeY = tY;
					eyeZ = tZ;
				}
			}
		}
		
		// go to the left
		if(e.getKeyCode() == KeyEvent.VK_A) {
			for(int i=0; i<speed; i++)
			{
				double tX = eyeX;
				double tY = eyeY;
				double tZ = eyeZ;
				
				eyeX += step*SIN(alpha);
				//eyeY += step*SIN(betha);
				eyeZ -= step*COS(alpha);
				
				if(!isInBounds())
				{
					eyeX = tX;
					eyeY = tY;
					eyeZ = tZ;
				}
			}
		}
		
		// go to the right
		if(e.getKeyCode() == KeyEvent.VK_D) {
			for(int i=0; i<speed; i++)
			{
				double tX = eyeX;
				double tY = eyeY;
				double tZ = eyeZ;
				
				eyeX -= step*SIN(alpha);
				//eyeY -= step*SIN(betha);
				eyeZ += step*COS(alpha);
				
				if(!isInBounds())
				{
					eyeX = tX;
					eyeY = tY;
					eyeZ = tZ;
				}
			}
		}
    }
    
    // init all textures - load images
    public void initTextures()
    {
    	// load texture file
        try {
        	
        	
    		// load textures
    		floor=TextureIO.newTexture(new File( "floor.jpg" ),true);
    		wall=TextureIO.newTexture(new File( "wall.jpg" ),true);
    		sky=TextureIO.newTexture(new File( "sky.jpg" ),true);
    		//wall2=TextureIO.newTexture(new File( wall2t ),true);
    		ceil2=TextureIO.newTexture(new File( "ceil3.jpg" ),true);
    		floor2=TextureIO.newTexture(new File( "grass.jpg" ),true);
    		carpet=TextureIO.newTexture(new File( "parket1.gif" ),true);
    		books=TextureIO.newTexture(new File( "books.jpg" ),true);
    		shelf=TextureIO.newTexture(new File( "shelf.jpg" ),true);
    		pic=TextureIO.newTexture(new File( "pic.jpg" ),true);
    		//wall3=TextureIO.newTexture(new File( wall3t ),true);
    		//wall4=TextureIO.newTexture(new File( wall4t ),true);
    		//wall5=TextureIO.newTexture(new File( wall5t ),true);
    		//wall6=TextureIO.newTexture(new File( wall6t ),true);
    		cab=TextureIO.newTexture(new File( "cab.jpg" ),true);
    		trophy=TextureIO.newTexture(new File( "trophy.jpg" ),true);
    		black=TextureIO.newTexture(new File( "black.jpg" ),true);
    		fire=TextureIO.newTexture(new File( "fire.jpg" ),true);
    		ice=TextureIO.newTexture(new File( "ice.jpg" ),true);
    		fireRoom=TextureIO.newTexture(new File( "fire2.jpg" ),true);
    		iceRoom=TextureIO.newTexture(new File( "ice2.jpg" ),true);
    		sand=TextureIO.newTexture(new File( "sand.jpg" ),true);
    		sandRoom=TextureIO.newTexture(new File( "sand2.jpg" ),true);
    		hiveroom=TextureIO.newTexture(new File( "hive.jpg" ),true);
    		hive=TextureIO.newTexture(new File( "hive2.jpg" ),true);
    		
    		
    		
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    	
    }
 
    public void keyReleased(KeyEvent e) {
    }
 
    public void keyTyped(KeyEvent e) {
    }
 
    // exit program
    public static void exit(){
		animator.stop();
		frame.dispose();
		System.exit(0);
    }
    
    public boolean isInBounds()
    {
    	double upperLimit = 3000;
    	double lowerLimit = 0;
    	boolean isCorridor = true;
     	
    	if(eyeY > sizeY - delta || eyeY < 0 + delta)
    		return false;
    	if(eyeZ > sizeZ - delta || eyeZ < zUpperLimit + delta)
    		return false;
    	if(eyeZ < zUpperLimit + 9000)
    	{
    		System.out.println("win!!");
    		win = true;
    		if(eyeX < -3000 + delta || eyeX > 6000 - delta)
    			return false;
    	}
    	else
    	{
	    	for(int i=0; i<numOfRooms; i++)
	    	{
	    		if(eyeZ < upperLimit && eyeZ > lowerLimit)
	    			isCorridor = false;
	    		upperLimit-=4000;
	    		lowerLimit-=4000;
	    	}
	    	if(isCorridor)
	    	{
	    		if(eyeX < 1000 + delta || eyeX > 2000 - delta)
	    			return false;
	    	}
	    	else
	    		if(eyeX < delta || eyeX > 3000 - delta)
	    			return false;
    	}

    	return true;
    }
    
    public double distance(Vector v1, Vector v2)
    {
    	double toReturn = Math.pow(v1.x - v2.x, 2) + Math.pow(v1.y - v2.y, 2) + Math.pow(v1.z - v2.z, 2);
    	return Math.sqrt(toReturn);
    }
    
    // main function
    public static void main(String[] args) {
		canvas.addGLEventListener(new Game3D());
		frame.add(canvas);
		frame.setSize(1200, 750);
      //frame.setUndecorated(true);
      //frame.setExtendedState(Frame.MAXIMIZED_BOTH);
		frame.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
			    exit();
			}
		    });
		frame.setVisible(true);
		animator.start();
		canvas.requestFocus();
    }
}
